package com.example.service

import com.example.data.dbQuery
import com.example.data.entity.ASMRDetailEntity
import com.example.data.entity.ASMREntity
import com.example.data.entity.ASMREntity.id
import com.example.data.entity.ASMREntity.title
import com.example.data.entity.TagEntity
import com.example.data.model.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq

class ASMRService {
    ///////////////////////////////////////////////////////////////////////////
    // 管理员可以调用
    ///////////////////////////////////////////////////////////////////////////
    /*数量*/
    suspend fun asmrCount() = dbQuery {
        ASMREntity.selectAll().count()
    }

    suspend fun audioCount() = dbQuery {
        ASMRDetailEntity.selectAll().count()
    }

    suspend fun tagCount() = dbQuery {
        TagEntity.selectAll().count()
    }

    suspend fun getASMR(id: Int) = dbQuery {
        ASMREntity.selectAll().where { ASMREntity.id eq id }.map {
            resultRowToASMRForAdmin(it)
        }.first()
    }

    suspend fun addASMR(asmr: ASMRForAdmin, tags: String?) = dbQuery {
        ASMREntity.insert {
            it[id] = asmr.id
            it[title] = asmr.title
            if (asmr.name != null) it[name] = asmr.name
            if (asmr.img != null) it[img] = asmr.img
            if (asmr.img240 != null) it[img240] = asmr.img240
            it[rateCount] = asmr.rateCount
            it[rateAverage] = asmr.rateAverage
            it[createDate] = asmr.createDate
            if (asmr.cv != null) it[cv] = asmr.cv
            if (tags != null) it[tag] = tags
        }.insertedCount > 0
    }

    suspend fun editASMR(asmr: ASMRForAdmin, tags: String?) = dbQuery {
        ASMREntity.update({ ASMREntity.id eq asmr.id }) {
            it[title] = asmr.title
            if (asmr.name != null) it[name] = asmr.name
            if (asmr.img != null) it[img] = asmr.img
            if (asmr.img240 != null) it[img240] = asmr.img240
            it[rateCount] = asmr.rateCount
            it[rateAverage] = asmr.rateAverage
            it[createDate] = asmr.createDate
            if (asmr.cv != null) it[cv] = asmr.cv
            if (tags != null) it[tag] = tags
        } > 0
    }

    suspend fun deleteASMR(id: Int) = dbQuery {
        ASMREntity.deleteWhere { ASMREntity.id eq id } > 0
    }

    /*分类*/
    suspend fun getTag(id: Int) = dbQuery {
        TagEntity.selectAll().where { TagEntity.id eq id }.map {
            Tag(it[TagEntity.id], it[TagEntity.name])
        }.first()
    }

    suspend fun addTag(name: String) = dbQuery {
        TagEntity.insert {
            it[TagEntity.name] = name
        }.insertedCount > 0
    }

    suspend fun editTag(name: String, id: Int) = dbQuery {
        TagEntity.update({ TagEntity.id eq id }) {
            it[TagEntity.name] = name
        } > 0
    }

    suspend fun deleteTag(id: Int) = dbQuery {
        TagEntity.deleteWhere { TagEntity.id eq id } > 0
    }

    suspend fun addDetail(asmrDetail: ASMRDetail) = dbQuery {
        ASMRDetailEntity.insert {
            it[title] = asmrDetail.title
            it[url] = asmrDetail.url
            it[asmrId] = asmrDetail.asmrId
        }.insertedCount > 0
    }

    suspend fun editDetail(asmrDetail: ASMRDetail) = dbQuery {
        ASMRDetailEntity.update({ ASMRDetailEntity.id eq asmrDetail.id }) {
            it[title] = asmrDetail.title
            it[url] = asmrDetail.url
            it[asmrId] = asmrDetail.asmrId
        } > 0
    }

    suspend fun deleteDetail(id: Int) = dbQuery {
        ASMRDetailEntity.deleteWhere { ASMRDetailEntity.id eq id } > 0
    }

    ///////////////////////////////////////////////////////////////////////////
    // 普通用户可调用
    ///////////////////////////////////////////////////////////////////////////
    suspend fun getAllASMRIdTitle() = dbQuery {
        ASMREntity.selectAll().map {
            Pair(it[id], it[title])
        }
    }

    suspend fun getAllByTag(page: Int, pageSize: Int, id: Int): List<ASMR> {//todo 暂时这样处理
        return dbQuery {
            val tagMap = getTag()
            val map = ASMREntity.selectAll()
//                .where {
//                ASMREntity.tag like "${id}%"
//            }.limit(pageSize, (pageSize * (page - 1)).toLong())
                .map {
                    val list = mutableListOf<Tag>()
                    val tag = it[ASMREntity.tag]
                    if (tag.isNotEmpty()) {
                        val split = tag.split(",").map { a -> a.toInt() }
                        split.forEach { a ->
                            list.add(Tag(a, tagMap[a] ?: ""))
                        }
                    }
                    resultRowToASMR(it, list)
                }
            val i = pageSize * (page - 1)

            val apply = map.filter {
                for (tag in it.tags) {
                    if (tag.id == id) {
                        return@filter true
                    }
                }
                return@filter false
            }.apply {
                if (size > pageSize) {
                    if (size - i > pageSize) {
                        this.subList(i, pageSize)
                    } else {
                        this.subList(i, size)
                    }
                }
            }
            if (i > apply.size) {
                emptyList<ASMR>()
            } else {
                apply
            }
        }
    }

    suspend fun getAll(page: Int, pageSize: Int, title: String?): List<ASMR> {
        return dbQuery {
            val tagMap = getTag()
            ASMREntity.selectAll().apply {
                if (title != null) {
                    this.where {
                        val toIntOrNull = title.toIntOrNull()
                        if (toIntOrNull == null) {
                            (ASMREntity.title like "%${title}%")
                        } else {
                            (ASMREntity.id eq toIntOrNull)
                        }
                    }
                }
            }.limit(pageSize, (pageSize * (page - 1)).toLong()).map {
                val list = mutableListOf<Tag>()
                val tag = it[ASMREntity.tag]
                if (tag.isNotEmpty()) {
                    val split = tag.split(",").map { a -> a.toInt() }
                    split.forEach { a ->
                        list.add(Tag(a, tagMap[a] ?: ""))
                    }
                }
                resultRowToASMR(it, list)
            }
        }
    }

    suspend fun getAll(page: Int, pageSize: Int, order: String?, sort: String?): List<ASMR> {
        return dbQuery {
            val tagMap = getTag()
            ASMREntity.selectAll().apply {
                if (order != null && sort != null) {
                    when (order) {
                        "release" -> {
                            (orderByExpressions as MutableList).add(ASMREntity.createDate to SortOrder.valueOf(sort))
                        }

                        "rate_average_2dp" -> {
                            (orderByExpressions as MutableList).add(ASMREntity.rateAverage to SortOrder.valueOf(sort))
                        }

                        "rate_count" -> {
                            (orderByExpressions as MutableList).add(ASMREntity.rateCount to SortOrder.valueOf(sort))
                        }
                    }
                }
            }.limit(pageSize, (pageSize * (page - 1)).toLong()).map {
                val list = mutableListOf<Tag>()
                val tag = it[ASMREntity.tag]
                if (tag.isNotEmpty()) {
                    val split = tag.split(",").map { a -> a.toInt() }
                    split.forEach { a ->
                        list.add(Tag(a, tagMap[a] ?: ""))
                    }
                }
                resultRowToASMR(it, list)
            }
        }
    }


    suspend fun getASMRByTagString(ids: String): List<ASMR> {
        return dbQuery {
            val tagMap = getTag()
            ASMREntity.selectAll().limit(100).where {
                ASMREntity.tag regexp "\\b($ids)\\b"
            }.map {
                val list = mutableListOf<Tag>()
                val tag = it[ASMREntity.tag]
                if (tag.isNotEmpty()) {
                    val split = tag.split(",").map { a -> a.toInt() }
                    split.forEach { a ->
                        list.add(Tag(a, tagMap[a] ?: ""))
                    }
                }
                resultRowToASMR(it, list)
            }
        }
    }

    /*详情*/
    suspend fun getDetail(id: Int): List<ASMRDetail> {
        return dbQuery {
            ASMRDetailEntity.select { ASMRDetailEntity.asmrId eq id }.map {
                ASMRDetail(
                    it[ASMRDetailEntity.id],
                    it[ASMRDetailEntity.asmrId],
                    it[ASMRDetailEntity.title],
                    it[ASMRDetailEntity.url]
                )
            }
        }
    }


    suspend fun getAllDetail(
        page: Int,
        pageSize: Int,
        asmrId: Int?,
        title: String?
    ): List<ASMRDetailForAdmin> {
        return dbQuery {
            val allASMRIdTitle = getAllASMRIdTitle()
            ASMRDetailEntity.selectAll().where {
                (if (asmrId != null) ASMRDetailEntity.asmrId eq asmrId else Op.TRUE) and
                        (if (title != null) ASMRDetailEntity.title like "%${title}%" else Op.TRUE)
            }.limit(pageSize, (pageSize * (page - 1)).toLong())
                .map {
                    val asmrId = it[ASMRDetailEntity.asmrId]
                    ASMRDetailForAdmin(
                        it[ASMRDetailEntity.id],
                        asmrId,
                        allASMRIdTitle.first { a -> a.first == asmrId }.second,
                        it[ASMRDetailEntity.title],
                        it[ASMRDetailEntity.url]
                    )
                }
        }
    }

    /*标签*/
    suspend fun getTagStringByIds(ids: Collection<Int>): List<String> {
        return dbQuery {
            ASMREntity.selectAll().limit(100).where {
                ASMREntity.id inList ids
            }.map {
                it[ASMREntity.tag]
//                resultRowToASMR(it, list)
            }
        }
    }

    suspend fun getTags(page: Int, pageSize: Int, name: String?) = dbQuery {
        TagEntity.selectAll().apply {
            if (name != null) {
                this.where {
                    TagEntity.name like "%${name}%"
                }
            }
        }.limit(pageSize, (pageSize * (page - 1)).toLong())
            .map { Tag(it[TagEntity.id], it[TagEntity.name]) }
    }

    suspend fun getTags() = dbQuery {
        TagEntity.selectAll().map { Tag(it[TagEntity.id], it[TagEntity.name]) }
    }

    private fun getTag(): Map<Int, String> {
        return TagEntity.selectAll().associate { it[TagEntity.id] to it[TagEntity.name] }
    }

    /**/
    private fun resultRowToASMR(row: ResultRow, tag: List<Tag>) = ASMR(
        id = row[ASMREntity.id],
        title = row[ASMREntity.title],
        img = row[ASMREntity.img],
        img240 = row[ASMREntity.img240],
        name = row[ASMREntity.name],
        rateCount = row[ASMREntity.rateCount],
        rateAverage = row[ASMREntity.rateAverage],
        createDate = row[ASMREntity.createDate],
        cv = row[ASMREntity.cv],
        tags = tag
    )

    private fun resultRowToASMRForAdmin(row: ResultRow) = ASMRForAdmin(
        id = row[ASMREntity.id],
        title = row[ASMREntity.title],
        img = row[ASMREntity.img],
        img240 = row[ASMREntity.img240],
        name = row[ASMREntity.name],
        rateCount = row[ASMREntity.rateCount],
        rateAverage = row[ASMREntity.rateAverage],
        createDate = row[ASMREntity.createDate],
        cv = row[ASMREntity.cv],
        tags = row[ASMREntity.tag].let {
            if (it.isNotEmpty()) {
                it.split(",").map { it.toInt() }
            } else {
                null
            }
        },
    )

    private fun resultRowToTag(row: ResultRow) = Tag(
        id = row[TagEntity.id],
        name = row[TagEntity.name],
    )

}